import vnode from "./vnode";

// patch 方法
export default function (oldVnode, newVnode) {
  if (oldVnode.sel == "" || oldVnode.sel == undefined) {
    // 传入的是DOM 转换为虚拟DOM
    oldVnode = vnode(oldVnode.tagName.toLowerCase, {}, [], undefined, oldVnode);
  }
  console.log(oldVnode);

  if (sameVnode(oldVnode, newVnode)) {
    // 同一个节点，精细比较
    patchVnode(oldVnode, newVnode);
  } else {
    // 暴力插入新的，删除旧的
    // 在老的DOM父级下，插入新的vnode
    let elm = createElement(newVnode);
    oldVnode?.elm?.parentNode?.insertBefore(elm, oldVnode.elm);
    // 删除老节点
    oldVnode.elm.parentNode.removeChild(oldVnode.elm);
  }
}

// 判断是否为同一个节点
function sameVnode(oldVnode, newVnode) {
  return oldVnode.key === newVnode.key && oldVnode.sel === newVnode.sel;
}

// 创建老节点的map来查找数据
function createKeyToOldIdx(oldVnodeCh, oldStartIdx, oldEndIdx) {
  let map = {}
  for (let index = oldStartIdx; index < oldEndIdx; index++) {
    const key = oldVnodeCh[index].key
    if(key !== undefined) {
      map[key] = index
    }
  }
  return map
}

/**
 * 将 vNode 递归创建成真是DOM
 * @param {*} vnode 创建的虚拟节点
 */
function createElement(vnode) {
  let domNode = document.createElement(vnode.sel);

  if (
    vnode.text != "" &&
    (vnode.children == undefined || vnode.children.length == 0)
  ) {
    // 单个节点上树，递归出口
    domNode.innerText = vnode.text;
  } else if (Array.isArray(vnode.children) && vnode.children.length > 0) {
    // 递归创建子节点
    let len = vnode.children.length;
    for (let index = 0; index < len; index++) {
      domNode.appendChild(createElement(vnode.children[index]));
    }
  }
  vnode.elm = domNode;

  return vnode.elm;
}

/**
 * 父节点为同一个，子节点进行精细化比较
 * @param {*} oldVnode 
 * @param {*} newVnode 
 * @returns 
 */
function patchVnode(oldVnode, newVnode) {
  if (oldVnode === newVnode) return;
  if (newVnode.text && (!newVnode.children || newVnode.children.length === 0)) {
    // newVnode和oldVnode是否相同
    if (newVnode.text !== oldVnode.text) {
      oldVnode.elm.innerText = newVnode.text;
    }
  } else {
    if (!oldVnode.children || oldVnode.children.length === 0) {
      oldVnode.elm.innerText = "";
      for (let index = 0; index < newVnode.children.length; index++) {
        const dom = createElement(newVnode.children[index]);
        oldVnode?.elm?.parentNode?.appendChild(dom);
      }
    } else {
      // 新老都有children
      updateChildren(oldVnode.elm, oldVnode.children, newVnode.children)

      /**
       * 经典Diff算法核心：我们写代码时，容易写出的更新的四种情况，提出了四个指针
       * 1. 新前 和 旧前：命中 新前++ ，旧前++
       * 2. 新后 和 旧后：命中 新后-- ，旧后--
       * 3. 新后 和 旧前：命中 移动节点：新前指向的节点，移动的旧后之后  新后--   旧前++
       * 4. 新前 和 旧后：命中 移动节点：新前指向的节点，移动的旧前之前
       * 
       * 命中后不再进行判断
       * 如果都没命中，循环查找：
       *        找到后置，将找到的旧目标置为undefined，移动到 旧前
       *        找不到 移动到 旧前
       * 
       * 循环完毕后：
       *    老节点还有剩余节点，说明他们是要被删除的节点；
       *    新节点还有剩余节点，说明新前和到旧后的节点直接插入到 旧后 中；
       * 
       */

    }
  }
}


function updateChildren(parentElm ,oldVnodeCh, newVnodeCh) {
  let newStartIdx = 0;    // 新前
  let oldStartIdx = 0;    // 旧前
  let newEndIdx = newVnodeCh.length - 1;   // 新后
  let oldEndIdx = oldVnodeCh.length - 1;   // 旧后

  let newStartVnode = newVnodeCh[newStartIdx]   // 新前节点
  let oldStartVnode = oldVnodeCh[oldStartIdx]   // 旧前节点
  let newEndVnode = newVnodeCh[newEndIdx]   // 新后节点
  let oldEndVnode = oldVnodeCh[oldEndIdx]   // 旧后节点



  let oldKeyToIdx = null

  while (newStartIdx <= newEndIdx && oldStartIdx <= oldEndIdx) {
    // console.log('☆');
    if(!newStartVnode) {
      newStartVnode = newVnodeCh[++newStartIdx]
    } else if(!newEndVnode) {
      newEndVnode = newVnodeCh[--newEndIdx]
    } else if(!oldStartVnode) {
      oldStartVnode = oldVnodeCh[++oldStartIdx]
    } else if(!oldEndVnode) {
      oldEndVnode = oldVnodeCh[--oldEndIdx]
    }else if(sameVnode(newStartVnode, oldStartVnode)) {   // 新前 和 旧前：命中
      console.log('命中1');
      patchVnode(oldStartVnode, newStartVnode)
      newStartVnode = newVnodeCh[++newStartIdx]
      oldStartVnode = oldVnodeCh[++oldStartIdx]
    } else if(sameVnode(newEndVnode, oldEndVnode)) {    // 新后 和 旧后：命中
      console.log('命中2');
      patchVnode(oldEndVnode, newEndVnode)
      newEndIdx--
      newEndVnode = newVnodeCh[newEndIdx]
      oldEndIdx--
      oldEndVnode = oldVnodeCh[oldEndIdx]
    } else if(sameVnode(newEndVnode, oldStartVnode)) {  // 新后 和 旧前：命中
      console.log('命中3');
      patchVnode(oldStartVnode, newEndVnode)
      // 旧前 指向的节点，移动到旧后之后
      parentElm.insertBefore(oldStartVnode.elm, oldEndVnode.elm.nextSibling)
      newEndIdx--
      newEndVnode = newVnodeCh[newEndIdx]
      oldStartIdx++
      oldStartVnode = oldVnodeCh[oldStartIdx]

    } else if(sameVnode(newStartVnode, oldEndVnode)) {  // 新前 和 旧后：命中
      console.log('命中4');
      patchVnode(oldStartVnode, newStartVnode)
      // 旧后 指向的节点，移动的旧前之前
      parentElm.insertBefore(oldEndVnode.elm, oldStartVnode.elm)
      newStartIdx++
      newStartVnode = newVnodeCh[newStartIdx]
      oldEndIdx--
      oldEndVnode = oldVnodeCh[oldEndIdx]

    } else {
      // 使用map的方式来查找
      if(!oldKeyToIdx) {
        oldKeyToIdx = createKeyToOldIdx(oldVnodeCh, oldStartIdx, oldEndIdx)
      }

      const idxInOld = oldKeyToIdx[newStartVnode.key]   // 老节点的 index

      if(!idxInOld) {    // 老节点中找不到  新节点
        // 将新节点添加到 旧前 之前
        parentElm.insertBefore(createElement(newStartVnode), oldStartVnode.elm)
      } else {   // 找到老节点
        const elmToMove = oldVnodeCh[idxInOld]    // 需要移动的节点
        
        patchVnode(oldVnodeCh[idxInOld], newStartVnode)   // 看看新老节点中的里头的内容是否改变

        oldVnodeCh[idxInOld] = undefined    // 移动后置为undefined，证明处理过

        parentElm.insertBefore(elmToMove.elm, oldStartVnode.elm)

      }
      newStartVnode = newVnodeCh[++newStartIdx]
    }
    
  }

  if(newStartIdx <= newEndIdx) {    // 新节点有剩余
    // let lastVnode = []    // 剩余节点
    for (let index = newStartIdx; index <= newEndIdx; index++) {
      // lastVnode.push(newVnodeCh[index])
      parentElm.appendChild(createElement(newVnodeCh[index]));
    }

  } else if(oldStartIdx <= oldEndIdx) {    // 老节点有剩余
    for (let index = oldStartIdx; index <= oldEndIdx; index++) {
      // lastVnode.push(newVnodeCh[index])
      if(oldVnodeCh[index] && oldVnodeCh[index].elm) {
        parentElm.removeChild(oldVnodeCh[index].elm);
      }
    }
  }




}